﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MapEntities;
using DALEntities;
using ServerPlatform.DataProvider;
using System.Drawing;
using ServerPlatform.Exceptions;


namespace ServerPlatform.Mapping
{
    public class TableFieldProperties
    {
        private double maxvalue, minvalue, avarage;
        private List<string> stringlist;

        public TableFieldProperties(double xmaxvalue, double xminvalue, double xavarage)
        {
            maxvalue = xmaxvalue;
            minvalue = xminvalue;
            avarage = xavarage;
        }
        public TableFieldProperties()
        {
            maxvalue = 0;
            minvalue = 0;
            avarage = 0;
            stringlist = null;
        }

        public double MaxValue
        {
            get { return maxvalue; }

            set { maxvalue = value; }
        }
        public double MinValue
        {
            get { return minvalue; }

            set { minvalue = value; }
        }
        public double Avarage
        {
            get { return avarage; }

            set { avarage = value; }
        }
        public List<string> StringList
        {
            get { return stringlist; }

            set { stringlist = value; }
        }
    }
    public class MapProcessor
    {
        private MapEntities.MapDataRequest Request;
        private MapEntities.MapDataResult Result;

        public MapProcessor(MapEntities.MapDataRequest request)
        {
            Request = request;
            Result = new MapDataResult();
            Result.RequestID = request.Id;
            Result.MapProperties = request.MapProperties;
        }

        public MapDataResult GetMapData()
        {
            ProcessShapes();
            ProcessSymbols();
            return Result;
        }
        private void ProcessSymbols()
        {
            foreach (var rule in Request.SymbolBindingRuleDataList)
            {
                Layer xlayer = Result.MapProperties.Layers.SingleOrDefault(p => p.Name == rule.CustomLayer.Trim());
                if (xlayer != null)
                    if (xlayer.Visible)
                    {
                        ViewResult list = DataProviderFramework.GetViewResult(rule.ListingRule.Request);
                        ViewResult items = DataProviderFramework.GetViewResult(rule.BindingRule.Request);
                        LookupHelper helper = new LookupHelper(items.Data, rule.BindingRule.KeyField, rule.BindingRule.ValueField);
                        foreach (var item in list.Data)
                        {
                            Symbol symbol = new Symbol();
                            object keyValue = DataProviderFramework.Eval(item, rule.ListingRule.KeyField);
                            object itemValue = helper.Lookup(keyValue);
                            if (itemValue != null)
                            {
                                SetSymbolVisualization(symbol, rule, itemValue);
                                try
                                { symbol.Location = new GeoPoint(double.Parse(DataProviderFramework.Eval(itemValue, rule.LatitudeField).ToString()), double.Parse(DataProviderFramework.Eval(itemValue, rule.LongitudeField).ToString())); }
                                catch (FormatException) { throw new BusinessException("Data contains invalid value"); }
                                SetLayerItemProperties(symbol, rule, itemValue);
                                SetLayerItemBehaviour(symbol, rule.Behaviour, itemValue);
                                Result.Symbols.Add(symbol);

                            }

                        }
                    }
            }
        }

        private void SetSymbolVisualization(Symbol symbol, SymbolItemBindingRule rule, object itemValue)
        {
            object currentValue = DataProviderFramework.Eval(itemValue, rule.BindingRule.ValueField);
            SymbolItemRangeList xrlist = rule.Visualization.RangeList;
            SymbolItemRange xitemrange = null;
            if (xrlist.RangeDataType == DataType.String) currentValue = currentValue.ToString().Trim();
            try
            { if (xrlist.RangeDataType == DataType.DateTime) currentValue = DateTime.Parse(currentValue.ToString().Trim()); }
            catch (FormatException) { throw new BusinessException("Data contains invalid value"); }
            if (xrlist.InRange(currentValue, out xitemrange))
            {
                symbol.SymbolTypeName = xitemrange.Symbol;
            }
            else
            {
                symbol.SymbolTypeName = rule.Visualization.DefaultSymbolType;
            }
        }

        private void ProcessShapes()
        {
            foreach (var rule in Request.ShapeBindingRuleDataList)
            {
                Layer xlayer = Result.MapProperties.Layers.SingleOrDefault(p => p.Name == rule.CustomLayer.Trim());
                if (xlayer != null)
                    if (xlayer.Visible)
                    {
                       ViewResult list = DataProviderFramework.GetViewResult(rule.ListingRule.Request);
                        ViewResult items = DataProviderFramework.GetViewResult(rule.BindingRule.Request);
                        LookupHelper helper = new LookupHelper(items.Data, rule.BindingRule.KeyField, rule.BindingRule.ValueField);
                        double maxvalue = double.MinValue, minvalue = double.MaxValue, totalvalue = 0, count = 0, average = 0;
                        List<LookupItem> valueList = helper.Cache.Select(p => p.Value).ToList();
                        count = (double)valueList.Count;
                        if (count == 0) throw new BusinessException("There is no data in the " + rule.BindingRule.ValueField + " field");
                        DataType xdatatype = rule.Visualization.BackgroundVisialization.DataType;
                        TableFieldProperties xproperties = null;
                        switch (xdatatype)
                        {
                            case DataType.String:
                                {
                                    List<string> xlist = new List<string>();
                                    foreach (var x in valueList)
                                    {
                                        if (xlist.IndexOf(x.value.ToString().Trim()) == -1) xlist.Add(x.value.ToString().Trim());
                                    }
                                    xproperties = new TableFieldProperties();
                                    xproperties.StringList = xlist;
                                    break;
                                }
                            case DataType.DateTime:
                                {
                                    DateTime max = DateTime.MinValue, min = DateTime.MaxValue;
                                    foreach (var x in valueList)
                                    {
                                        DateTime xtemp;
                                        try { xtemp = DateTime.Parse(x.value.ToString()); }
                                        catch (FormatException) { throw new BusinessException("Data contains invalid value"); }
                                        if (DateTime.Compare(xtemp, max) > 0) max = xtemp;
                                        if (DateTime.Compare(xtemp, min) < 0) min = xtemp;
                                    }
                                    xproperties = new TableFieldProperties(max.ToOADate(), min.ToOADate(), ((max.ToOADate() + min.ToOADate()) / 2));
                                    break;
                                }
                            case DataType.Blob: break;
                            case DataType.Spatial: break;
                            case DataType.Unknown: break;
                            default:
                                {
                                    double xvalue, xtemp;
                                    foreach (var x in valueList)
                                    {
                                        if (x.value is double)
                                        {
                                            xvalue = (double)x.value;
                                            totalvalue += (double)x.value;
                                        }
                                        else
                                        {

                                            try { xtemp = double.Parse(x.value.ToString().Trim()); }
                                            catch (FormatException) { throw new BusinessException("Data contains invalid value"); }
                                            xvalue = xtemp;
                                            totalvalue += xtemp;
                                        }
                                        if (maxvalue < xvalue) maxvalue = xvalue;
                                        if (minvalue > xvalue) minvalue = xvalue;
                                    }
                                    average = totalvalue / count;
                                    xproperties = new TableFieldProperties(maxvalue, minvalue, average);
                                    break;
                                }
                        }
                        foreach (var item in list.Data)
                        {
                            Shape shape = DataProviderFramework.GetShape(rule.ListingRule.Request.ProviderName, item, rule);
                            if (shape != null)
                            {
                                object keyValue = DataProviderFramework.Eval(item, rule.ListingRule.KeyField);
                                object itemValue = helper.Lookup(keyValue);
                                if (itemValue != null)
                                {
                                    SetShapeVisualization(shape, rule, itemValue, helper, xproperties);
                                }
                                else
                                {
                                    shape.Transparency = TransparencyLevel.None;
                                    shape.BackgroundColor = ColorTranslator.ToHtml(Color.White);
                                }
                                SetLayerItemProperties(shape, rule, itemValue);
                                SetLayerItemBehaviour(shape, rule.Behaviour, itemValue);
                                Result.Shapes.Add(shape);

                            }
                        }
                    }
            }
        }

        private void SetLayerItemBehaviour(LayerItem item, LayerItemBehaviour behaviour, object container)
        {
            if (item is Shape)
            {
                Shape s = (Shape)item;
                s.Behaviour.LeftMouseClick.ZoomToLevel = behaviour.LeftMouseClick.ZoomToLevel;
                s.Behaviour.LeftMouseClick.RedirectAddress = DataProviderFramework.EvalWithFields(container, behaviour.LeftMouseClick.RedirectAddress);

                s.Behaviour.RightMouseClick.ZoomToLevel = behaviour.RightMouseClick.ZoomToLevel;
                s.Behaviour.RightMouseClick.RedirectAddress = DataProviderFramework.EvalWithFields(container, behaviour.RightMouseClick.RedirectAddress);

                s.Behaviour.DoubleMouseClick.ZoomToLevel = behaviour.DoubleMouseClick.ZoomToLevel;
                s.Behaviour.DoubleMouseClick.RedirectAddress = DataProviderFramework.EvalWithFields(container, behaviour.DoubleMouseClick.RedirectAddress);
                s.Behaviour.ItemTransparencyLevel = behaviour.ItemTransparencyLevel;
            }
            else if (item is Symbol)
            {
                Symbol s = (Symbol)item;
                s.Behaviour.LeftMouseClick.ZoomToLevel = behaviour.LeftMouseClick.ZoomToLevel;
                s.Behaviour.LeftMouseClick.RedirectAddress = DataProviderFramework.EvalWithFields(container, behaviour.LeftMouseClick.RedirectAddress);

                s.Behaviour.RightMouseClick.ZoomToLevel = behaviour.RightMouseClick.ZoomToLevel;
                s.Behaviour.RightMouseClick.RedirectAddress = DataProviderFramework.EvalWithFields(container, behaviour.RightMouseClick.RedirectAddress);

                s.Behaviour.DoubleMouseClick.ZoomToLevel = behaviour.DoubleMouseClick.ZoomToLevel;
                s.Behaviour.DoubleMouseClick.RedirectAddress = DataProviderFramework.EvalWithFields(container, behaviour.DoubleMouseClick.RedirectAddress);
                s.Behaviour.ItemTransparencyLevel = behaviour.ItemTransparencyLevel;
            }
        }


        public Color ColorFromAhsb(int a, float h, float s, float b)
        {

            if ((0 > a || 255 < a) || (0f > h || 360f < h) || (0f > s || 1f < s) || (0f > b || 1f < b))
            {
                throw new TechnicalException("Color properties are out of bound");
            }


            if (0 == s)
            {
                return Color.FromArgb(a, Convert.ToInt32(b * 255),
                  Convert.ToInt32(b * 255), Convert.ToInt32(b * 255));
            }

            float fMax, fMid, fMin;
            int iSextant, iMax, iMid, iMin;

            if (0.5 < b)
            {
                fMax = b - (b * s) + s;
                fMin = b + (b * s) - s;
            }
            else
            {
                fMax = b + (b * s);
                fMin = b - (b * s);
            }

            iSextant = (int)Math.Floor(h / 60f);
            if (300f <= h)
            {
                h -= 360f;
            }
            h /= 60f;
            h -= 2f * (float)Math.Floor(((iSextant + 1f) % 6f) / 2f);
            if (0 == iSextant % 2)
            {
                fMid = h * (fMax - fMin) + fMin;
            }
            else
            {
                fMid = fMin - h * (fMax - fMin);
            }

            iMax = Convert.ToInt32(fMax * 255);
            iMid = Convert.ToInt32(fMid * 255);
            iMin = Convert.ToInt32(fMin * 255);

            switch (iSextant)
            {
                case 1:
                    return Color.FromArgb(a, iMid, iMax, iMin);
                case 2:
                    return Color.FromArgb(a, iMin, iMax, iMid);
                case 3:
                    return Color.FromArgb(a, iMin, iMid, iMax);
                case 4:
                    return Color.FromArgb(a, iMid, iMin, iMax);
                case 5:
                    return Color.FromArgb(a, iMax, iMin, iMid);
                default:
                    return Color.FromArgb(a, iMax, iMid, iMin);
            }
        }

        public Color SetColorRange(double xmaxvalue, double xminvalue, KnownColor xcolor, object xcurrentvalue, double xavarage, DataType xdatatype)
        {
            double xval;
            if (xdatatype == DataType.DateTime)
            {
                try { xval = DateTime.Parse(xcurrentvalue.ToString()).ToOADate(); }
                catch (FormatException) { throw new BusinessException("Data contains invalid value"); }
            }
            else
            {
                try { xval = double.Parse(xcurrentvalue.ToString().Trim()); }
                catch (FormatException) { throw new BusinessException("Data contains invalid value"); }
            }
            Color xselectedcolor = Color.FromKnownColor(xcolor);
            float xhue, xsaturation, xbrightness;
            byte xalpha = xselectedcolor.A;
            xhue = xselectedcolor.GetHue();
            xsaturation = xselectedcolor.GetSaturation();
            xbrightness = xselectedcolor.GetBrightness();
            if ((xavarage < xmaxvalue) && (xavarage > xminvalue))
            {

                if (xval < xavarage)
                {
                    xbrightness = (float)(((xval - xminvalue) * 100) / (xavarage - xminvalue)) + 25;
                }
                else
                {
                    xbrightness = (float)(((xval - xavarage) * 100) / (xmaxvalue - xavarage)) + 125;
                }
                return ColorFromAhsb(xalpha, xhue, xsaturation, 1 - (xbrightness / 255));
            }
            else return ColorFromAhsb(xalpha, xhue, xsaturation, xbrightness);
        }

        public Color SetColorRangeForString(List<string> xlist, string xcurrentvalue, KnownColor xcolor)
        {
            Color xselectedcolor = Color.FromKnownColor(xcolor);
            float xhue, xsaturation, xbrightness;
            byte xalpha = xselectedcolor.A;
            xhue = xselectedcolor.GetHue();
            xsaturation = xselectedcolor.GetSaturation();
            xbrightness = 25 + ((200 / xlist.Count) * (xlist.IndexOf(xcurrentvalue) + 1));
            return ColorFromAhsb(xalpha, xhue, xsaturation, 1 - (xbrightness / 255));
        }

        private void SetShapeVisualization(Shape shape, ShapeItemBindingRule rule, object itemValue, LookupHelper items, TableFieldProperties xtableproperties)
        {

            object currentValue = DataProviderFramework.Eval(itemValue, rule.BindingRule.ValueField);
            ShapeBackgroundRangeList xrlist = rule.Visualization.BackgroundVisialization.RangeList;
            if (xtableproperties == null) return;
            if (rule.Visualization.BackgroundVisialization.DataType == DataType.String) currentValue = currentValue.ToString().Trim();
            try { if (rule.Visualization.BackgroundVisialization.DataType == DataType.DateTime) currentValue = DateTime.Parse(currentValue.ToString().Trim()); }
            catch (FormatException) { throw new BusinessException("Data contains invalid value"); }
            if (rule.Visualization.BackgroundVisialization.Mode == ShapeBackgroundVisualizationMode.UserDefined)
            {
                ShapeBackgroundRange xmatchedrange = null;
                if (xrlist.InRange(currentValue, out xmatchedrange))
                {
                    shape.Transparency = xmatchedrange.Coloring.Level;
                    if (xtableproperties.StringList != null)
                    {
                        shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(Color.FromArgb(Color.FromKnownColor(xmatchedrange.Coloring.ColorName).ToArgb()));
                        return;
                    }
                    if (xmatchedrange.Coloring.Mode == ColoringMode.Gradient)
                    {
                        if (rule.Visualization.BackgroundVisialization.DataType == DataType.DateTime)
                            shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(SetColorRange(DateTime.Parse(xmatchedrange.Finish.ToString()).ToOADate(), DateTime.Parse(xmatchedrange.Start.ToString()).ToOADate(), xmatchedrange.Coloring.ColorName, currentValue, ((DateTime.Parse(xmatchedrange.Finish.ToString()).ToOADate() + DateTime.Parse(xmatchedrange.Start.ToString()).ToOADate()) / 2), rule.Visualization.BackgroundVisialization.DataType));
                        else shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(SetColorRange(double.Parse(xmatchedrange.Finish.ToString()), double.Parse(xmatchedrange.Start.ToString()), xmatchedrange.Coloring.ColorName, currentValue, xtableproperties.Avarage, rule.Visualization.BackgroundVisialization.DataType));
                    }
                    else shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(Color.FromArgb(Color.FromKnownColor(xmatchedrange.Coloring.ColorName).ToArgb()));
                }
                else
                {
                    shape.Transparency = rule.Visualization.BackgroundVisialization.RangeList.DefaulColoringInfo.Level;
                    if (xtableproperties.StringList != null)
                    {
                        shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(Color.FromArgb(Color.FromKnownColor(xrlist.DefaulColoringInfo.ColorName).ToArgb()));
                        return;
                    }
                    if (xrlist.DefaulColoringInfo.Mode == ColoringMode.Gradient)
                        shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(SetColorRange(xtableproperties.MaxValue, xtableproperties.MinValue, xrlist.DefaulColoringInfo.ColorName, currentValue, ((xtableproperties.MaxValue + xtableproperties.MinValue) / 2), rule.Visualization.BackgroundVisialization.DataType));
                    else shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(Color.FromArgb(Color.FromKnownColor(xrlist.DefaulColoringInfo.ColorName).ToArgb()));
                }
            }
            else
            {
                shape.Transparency = rule.Visualization.BackgroundVisialization.Automatic.ColoringInfo.Level;
                if (xtableproperties.StringList != null)
                {
                    if (rule.Visualization.BackgroundVisialization.Automatic.ColoringInfo.Mode == ColoringMode.Gradient)
                    {
                        shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(SetColorRangeForString(xtableproperties.StringList, currentValue.ToString().Trim(), rule.Visualization.BackgroundVisialization.Automatic.ColoringInfo.ColorName));
                        return;
                    }
                    else
                    {
                        shape.BackgroundColor = shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(Color.FromArgb(Color.FromKnownColor(rule.Visualization.BackgroundVisialization.Automatic.ColoringInfo.ColorName).ToArgb()));
                        return;
                    }
                }
                if (rule.Visualization.BackgroundVisialization.Automatic.ColoringInfo.Mode == ColoringMode.Gradient)
                    shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(SetColorRange(xtableproperties.MaxValue, xtableproperties.MinValue, rule.Visualization.BackgroundVisialization.Automatic.ColoringInfo.ColorName, currentValue, ((xtableproperties.MaxValue + xtableproperties.MinValue) / 2), rule.Visualization.BackgroundVisialization.DataType));
                else shape.BackgroundColor = System.Drawing.ColorTranslator.ToHtml(Color.FromArgb(Color.FromKnownColor(rule.Visualization.BackgroundVisialization.Automatic.ColoringInfo.ColorName).ToArgb()));
            }
        }

        private void SetLayerItemProperties(LayerItem item, LayerItemBindingRule rule, object container)
        {
            item.CustomLayer = rule.CustomLayer;
            item.ViewLayer = rule.ViewLayer;

            item.Text = DataProviderFramework.EvalWithFields(container, rule.TextBinding);
            item.Tooltip = DataProviderFramework.EvalWithFields(container, rule.TooltipBinding);

            item.Visible = container != null;
        }
    }
}
